package com.ionehe.group.common.uaa.ex.domain.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.google.common.base.Throwables;
import com.google.common.collect.Lists;
import com.ionehe.group.common.io.Response;
import com.ionehe.group.common.uaa.ex.model.UaaFunction;
import com.ionehe.group.common.user.c.sdk.uaa.component.DynamicSecurityMetadataSource;
import com.ionehe.group.common.uaa.ex.model.UaaCustomerFunction;
import com.ionehe.group.common.uaa.ex.dto.FunctionAddDTO;
import com.ionehe.group.common.uaa.ex.dto.FunctionUpdateDTO;
import com.ionehe.group.common.uaa.ex.enums.ErrorMessages;
import com.ionehe.group.common.uaa.ex.enums.FunctionStatus;
import com.ionehe.group.common.uaa.ex.exception.BusinessException;
import com.ionehe.group.common.uaa.ex.dao.CustomerFunctionDao;
import com.ionehe.group.common.uaa.ex.dao.FunctionDao;
import com.ionehe.group.common.uaa.ex.domain.service.FunctionWriteService;
import com.ionehe.group.common.uaa.ex.domain.service.UserCacheWriteService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import tk.mybatis.mapper.entity.Example;

import javax.annotation.Resource;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

import static com.google.common.base.Preconditions.checkArgument;

/**
 * Copyright (c) 2020 ionehe.com
 * Date: 2020/11/25
 * Time: 10:31 上午
 *
 * @author 2020年 <a href="mailto:a@ionehe.com">秀</a>
 */
@Service
@Slf4j
@Transactional(rollbackFor = Exception.class)
public class FunctionWriteServiceImpl implements FunctionWriteService {
    @Resource
    private FunctionDao functionDao;
    @Resource
    private CustomerFunctionDao customerFunctionDao;
    @Autowired
    private DynamicSecurityMetadataSource dynamicSecurityMetadataSource;
    @Autowired
    private UserCacheWriteService userCacheWriteService;

    @Override
    public Response<Boolean> upDownStand(List<Long> idList, Integer status) {
        checkArgument(CollUtil.isNotEmpty(idList) && Objects.nonNull(status));

        log.info("FunctionWriteService[]upDownStand[]start! idList:{}, status:{}", idList, status);
        try {
            // 功能信息
            List<UaaFunction> updateList = selectByIdList(idList);
            if (CollUtil.isEmpty(updateList) || updateList.size() != idList.size()) {
                return Response.no(ErrorMessages.UPDATE_FUNCTION_STATUS_FAIL.toString());
            }

            // 操作类型
            UaaFunction fun = new UaaFunction();
            switch (FunctionStatus.from(status)) {
                case ON_SHELF:
                    fun.setStatus(FunctionStatus.ON_SHELF.getValue());
                    break;
                case OFF_SHELF:
                    // 是否正在使用
                    inUsedCheck(idList);
                    fun.setStatus(FunctionStatus.OFF_SHELF.getValue());
                    break;
                default:
            }

            // 更新状态
            Boolean isSuccess = batchUpdate(fun, idList) >= 1;

            if (Boolean.TRUE.equals(isSuccess)) {
                // 刷新权限
                dynamicSecurityMetadataSource.clearDataSource();
                return Response.yes(true);
            }
            return Response.no(ErrorMessages.UPDATE_FUNCTION_STATUS_FAIL.toString());
        } catch (BusinessException e) {
            log.error("FunctionWriteService[]upDownStand[]error! cause:{}", e.getMessage());
            return Response.no(e.getMessage());
        } catch (Exception e) {
            log.error("FunctionWriteService[]upDownStand[]error! cause:{}", Throwables.getStackTraceAsString(e));
            return Response.no(ErrorMessages.UPDATE_FUNCTION_STATUS_FAIL.toString());
        }
    }

    @Override
    public Response<Boolean> updateSort(Long idUp, Long idDown) {
        checkArgument(Objects.nonNull(idUp) && Objects.nonNull(idDown));

        log.info("FunctionWriteService[]sort[]start! idUp:{}, idDown:{}", idUp, idDown);
        try {
            // 功能信息
            List<UaaFunction> functionList = selectByIdList(Lists.newArrayList(idUp, idDown));
            // Math.abs(functionList.get(0).getSort() - functionList.get(1).getSort()) != 1
            if (CollUtil.isEmpty(functionList) || functionList.size() != 2) {
                return Response.no(ErrorMessages.ILLEGAL_PARAMS.toString());
            }
            Map<Long, UaaFunction> functionMap = functionList.stream().collect(Collectors.toMap(UaaFunction::getId, fun -> fun));

            if (!Objects.equals(functionMap.get(idUp).getLevel(), functionMap.get(idDown).getLevel())) {
                return Response.no(ErrorMessages.ILLEGAL_PARAMS.toString());
            }

            //修改排序
            UaaFunction upFun = new UaaFunction();
            upFun.setId(idUp);
            upFun.setUpdateTime(new Date());
            upFun.setSort(functionMap.get(idDown).getSort());

            UaaFunction downFun = new UaaFunction();
            downFun.setId(idDown);
            downFun.setUpdateTime(new Date());
            downFun.setSort(functionMap.get(idUp).getSort());

            Boolean isSuccess = functionDao.updateByPrimaryKeySelective(upFun) == 1 && functionDao.updateByPrimaryKeySelective(downFun) == 1;
            if (Boolean.TRUE.equals(isSuccess)) {
                return Response.yes(true);
            }

            return Response.no(ErrorMessages.UPDATE_FUNCTION_FAIL.toString());
        } catch (Exception e) {
            log.error("FunctionWriteService[]sort[]error! cause:{}", Throwables.getStackTraceAsString(e));
            return Response.no(ErrorMessages.UPDATE_FUNCTION_FAIL.toString());
        }
    }

    @Override
    public Response<Boolean> addFunction(FunctionAddDTO functionAddDTO) {
        checkArgument(Objects.nonNull(functionAddDTO));

        log.info("FunctionWriteService[]add[]start! functionAddDTO:{}", functionAddDTO);
        try {
            checkName(functionAddDTO.getName());

            // 添加子级 （pId：当前操作的function id）
            if (Objects.isNull(functionAddDTO.getCurrentId())) {
                addChildLevel(functionAddDTO);
            } else {
                // 添加同级功能 （currentId：当前操作的function id）
                addSameLevel(functionAddDTO);
            }

            // 刷新权限
            dynamicSecurityMetadataSource.clearDataSource();
            return Response.yes(true);
        } catch (BusinessException e) {
            log.error("FunctionWriteService[]add[]error! cause::{}", Throwables.getStackTraceAsString(e));
            return Response.no(e.getMessage());
        } catch (Exception e) {
            log.error("FunctionWriteService[]add[]error! cause::{}", Throwables.getStackTraceAsString(e));
            return Response.no(ErrorMessages.INSERT_FUNCTION_FAIL.toString());
        }
    }

    @Override
    public Response<Boolean> updateFunction(FunctionUpdateDTO functionUpdateDTO) {
        checkArgument(Objects.nonNull(functionUpdateDTO));

        log.info("FunctionWriteService[]updateFunction[]start! functionUpdateDTO:{}", functionUpdateDTO);
        try {
            UaaFunction function = functionDao.selectByPrimaryKey(functionUpdateDTO.getId());
            if (Objects.isNull(function)) {
                return Response.no(ErrorMessages.ILLEGAL_PARAMS.toString());
            }
            checkName(functionUpdateDTO.getName());
            // 更新
            UaaFunction fun = new UaaFunction();
            BeanUtil.copyProperties(functionUpdateDTO, fun);
            Boolean isSuccess = functionDao.updateByPrimaryKeySelective(fun) == 1;
            if (Boolean.TRUE.equals(isSuccess)) {

                // 刷新权限
                dynamicSecurityMetadataSource.clearDataSource();
                // 刷新缓存
                if (FunctionStatus.ON_SHELF.getValue().equals(function.getStatus())) {
                    userCacheWriteService.delResourceModuleCache();
                }
                return Response.yes(true);
            }
            return Response.no(ErrorMessages.UPDATE_FUNCTION_FAIL.toString());
        } catch (BusinessException e) {
            log.error("FunctionWriteService[]updateFunction[]error! cause:{}", Throwables.getStackTraceAsString(e));
            return Response.no(e.getMessage());
        } catch (Exception e) {
            log.error("FunctionWriteService[]updateFunction[]error! cause:{}", Throwables.getStackTraceAsString(e));
            return Response.no(ErrorMessages.UPDATE_FUNCTION_FAIL.toString());
        }
    }

    @Override
    public Response<Boolean> batchUpdateStatus(List<Long> idList, Integer status) {
        checkArgument(CollUtil.isNotEmpty(idList) && Objects.nonNull(status));

        log.info("FunctionWriteService[]batchUpdateStatus[]start! idList:{}, status:{}", idList, status);
        try {
            // 功能信息
            List<UaaFunction> updateList = selectByIdList(idList);
            if (CollUtil.isEmpty(updateList) || updateList.size() != idList.size()) {
                return Response.no(ErrorMessages.UPDATE_FUNCTION_STATUS_FAIL.toString());
            }

            // 更新状态
            UaaFunction fun = new UaaFunction();
            fun.setStatus(status);
            Boolean isSuccess = batchUpdate(fun, idList) >= 1;

            if (Boolean.TRUE.equals(isSuccess)) {
                // 刷新权限
                dynamicSecurityMetadataSource.clearDataSource();
                return Response.yes(true);
            }
            return Response.no(ErrorMessages.UPDATE_FUNCTION_STATUS_FAIL.toString());
        } catch (BusinessException e) {
            log.error("FunctionWriteService[]batchUpdateStatus[]error! cause:{}", Throwables.getStackTraceAsString(e));
            return Response.no(e.getMessage());
        } catch (Exception e) {
            log.error("FunctionWriteService[]batchUpdateStatus[]error! cause:{}", Throwables.getStackTraceAsString(e));
            return Response.no(ErrorMessages.UPDATE_FUNCTION_STATUS_FAIL.toString());
        }
    }

    @Override
    public Response<Boolean> batchDelete(List<Long> idList) {
        checkArgument(CollUtil.isNotEmpty(idList));

        log.info("FunctionWriteService[]batchDelete[]start! idList:{}", idList);
        try {
            // 功能信息
            List<UaaFunction> updateList = selectByIdList(idList);
            if (CollUtil.isEmpty(updateList) || updateList.size() != idList.size()) {

                return Response.no(ErrorMessages.UPDATE_FUNCTION_STATUS_FAIL.toString());
            }

            // 是否正在使用中
            inUsedCheck(idList);

            // 逻辑删除
            UaaFunction fun = new UaaFunction();
            fun.setIsDelete(true);
            fun.setUpdateTime(new Date());

            Boolean isSuccess = batchUpdate(fun, idList) >= 1;

            if (Boolean.TRUE.equals(isSuccess)) {
                // 刷新权限
                dynamicSecurityMetadataSource.clearDataSource();
                return Response.yes(true);
            }
            return Response.no(ErrorMessages.UPDATE_FUNCTION_STATUS_FAIL.toString());
        } catch (BusinessException e) {
            log.error("FunctionWriteService[]batchDelete[]error! cause:{}", Throwables.getStackTraceAsString(e));
            return Response.no(e.getMessage());
        } catch (Exception e) {
            log.error("FunctionWriteService[]batchDelete[]error! cause:{}", Throwables.getStackTraceAsString(e));
            return Response.no(ErrorMessages.UPDATE_FUNCTION_STATUS_FAIL.toString());
        }
    }

    /**********************************              service函数分界线             **************************************/

    /**
     * 批量查询
     *
     * @param idList 需要更新的id集合
     * @return 查询结果集合
     */
    private List<UaaFunction> selectByIdList(List<Long> idList) {
        Example example = new Example(UaaFunction.class);
        example.createCriteria().andIn("id", idList);
        return functionDao.selectByExample(example);
    }

    /**
     * 批量更新
     *
     * @param function 需要更新的属性
     * @param idList   需要更新的id集合
     * @return 更新条数
     */
    private int batchUpdate(UaaFunction function, List<Long> idList) {
        Example example = new Example(UaaFunction.class);
        example.createCriteria().andIn("id", idList);
        return functionDao.updateByExampleSelective(function, example);
    }

    /**
     * 添加同级功能
     *
     * @param functionAddDTO 要添加功能的信息
     */
    private void addSameLevel(FunctionAddDTO functionAddDTO) {
        // 当前操作的function
        UaaFunction currentFunction = functionDao.selectByPrimaryKey(functionAddDTO.getCurrentId());
        if (Objects.isNull(currentFunction)) {
            throw new BusinessException(ErrorMessages.ILLEGAL_PARAMS.toString());
        }

        // 所有同级功能
        UaaFunction fun = new UaaFunction();
        fun.setLevel(currentFunction.getLevel());
        fun.setPid(currentFunction.getPid());
        fun.setIsDelete(false);
        List<UaaFunction> functionList = functionDao.select(fun);

        // 更新当前功能后的功能sort全+1
        List<UaaFunction> back4currentFunctionList = functionList.stream().filter(f -> f.getSort() > currentFunction.getSort()).collect(Collectors.toList());
        if (!CollUtil.isEmpty(back4currentFunctionList)) {
            back4currentFunctionList.forEach(f -> {
                f.setSort(f.getSort() + 1);
                f.setUpdateTime(new Date());
            });
            functionDao.batchUpdate(back4currentFunctionList);
        }

        // 插入功能到当前功能sort+1位置
        fun.setName(functionAddDTO.getName());
        fun.setType(functionAddDTO.getType());
        fun.setUrl(functionAddDTO.getUrl());
        fun.setStatus(FunctionStatus.OFF_SHELF.getValue());
        fun.setLevel(currentFunction.getLevel());
        fun.setSort(currentFunction.getSort() + 1);
        fun.setCreateTime(new Date());

        Boolean isSuccess = functionDao.insertSelective(fun) == 1;
        if (!Boolean.TRUE.equals(isSuccess)) {
            throw new BusinessException(ErrorMessages.INSERT_FUNCTION_FAIL.toString());
        }
    }

    /**
     * 添加子级功能
     *
     * @param functionAddDTO 要添加功能的信息
     */
    private void addChildLevel(FunctionAddDTO functionAddDTO) {
        // 父级权限
        UaaFunction parentFunction = functionDao.selectByPrimaryKey(functionAddDTO.getPid());
        if (Objects.isNull(parentFunction)) {
            throw new BusinessException(ErrorMessages.ILLEGAL_PARAMS.toString());
        }

        // 封装权限对象
        UaaFunction fun = new UaaFunction();
        fun.setPid(functionAddDTO.getPid());
        fun.setName(functionAddDTO.getName());
        fun.setType(functionAddDTO.getType());
        fun.setUrl(functionAddDTO.getUrl());
        fun.setStatus(FunctionStatus.OFF_SHELF.getValue());
        fun.setLevel(parentFunction.getLevel() + 1);
        fun.setSort(1);
        fun.setCreateTime(new Date());

        // 计算当前功能sort
        calculateSort(functionAddDTO, fun);

        // 新增当前权限
        Boolean isSuccess = functionDao.insertSelective(fun) == 1;
        if (!Boolean.TRUE.equals(isSuccess)) {
            throw new BusinessException(ErrorMessages.INSERT_FUNCTION_FAIL.toString());
        }

    }

    /**
     * url合法检查
     *
     * @param function 功能信息
     * @param url      url地址
     */
    private void checkUrl(UaaFunction function, String url) {
        if (StrUtil.isEmpty(url) || (Objects.nonNull(function) && StrUtil.equals(function.getUrl(), url))) {
            return;
        }
        UaaFunction fun = new UaaFunction();
        fun.setUrl(url);
        fun.setIsDelete(false);
        if (Objects.nonNull(functionDao.selectOne(fun))) {
            throw new BusinessException(ErrorMessages.FUNCTION_URL_EXIST.toString());
        }
    }

    /**
     * 权限是否正在被使用校验
     *
     * @param idList 功能id集合
     */
    private void inUsedCheck(List<Long> idList) {
        Example example = new Example(UaaCustomerFunction.class);
        example.createCriteria().andIn("functionId", idList);

        if (customerFunctionDao.selectCountByExample(example) != 0) {
            throw new BusinessException(ErrorMessages.FUNCTION_IS_USING.toString());
        }
    }

    /**
     * 计算当前功能sort
     *
     * @param functionAddDTO 要添加的功能
     * @param updateFun      更新的function
     */
    private void calculateSort(FunctionAddDTO functionAddDTO, UaaFunction updateFun) {
        UaaFunction fun = new UaaFunction();
        fun.setPid(functionAddDTO.getPid());
        fun.setIsDelete(false);
        List<UaaFunction> childFunctionList = functionDao.select(fun);
        if (!CollUtil.isEmpty(childFunctionList)) {
            updateFun.setSort(childFunctionList.size() + 1);
        }
    }

    /**
     * 权限名字非法校验
     *
     * @param name 权限名称
     */
    private void checkName(String name) {
        if (StrUtil.isNotEmpty(name) && name.length() > 16) {
            throw new BusinessException(ErrorMessages.FUNCTION_NAME_ILLEGAL.toString());
        }
    }
}
